﻿using DotNetCommon.Extensions;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace DotNetCommon
{
    /// <summary>
    /// 登录用户信息
    /// </summary>
    /// <remarks>
    /// 该对象储存三类信息：<para></para>
    /// <list type="bullet">
    /// <item>IdString：用户Id字符串，使用string类型表示，可兼容int/long/string/guid等key值，需要在程序启动时注入获取逻辑，参照：<seealso cref="User.RegisterIdString(Func{string})"/></item>
    /// <item>IsAuthenticated：用户是否登录，默认如果IdString为空则返回false，否则返回true，如果需要自定义，请调用：<seealso cref="User.RegisterIsAuthenticated(Func{bool})"/>；</item>
    /// <item>Properties：与用户关联的其他属性，支持异步上下文，根据需要使用，实现原理参照：<seealso cref="AsyncLocal{T}"/>；</item>
    /// </list>
    /// 使用示例：<para></para>
    /// 首先，在StartUp.cs的Configure中注入逻辑：<para></para>
    /// DotNetCommon.User.RegisterIdString(() => httpContextAccessor.HttpContext.User.Identity.Name);<para></para>
    /// DotNetCommon.User.RegisterIsAuthenticated(() => httpContextAccessor.HttpContext.User.Identity.IsAuthenticated);<para></para>
    /// 然后，在业务代码中使用：<para></para>
    /// var id=DotNetCommon.User.Current.IdString.To&lt;long&gt;();<para></para>
    /// var isAuth=DotNetCommon.User.Current.IsAuthenticated;<para></para>
    /// </remarks>
    public class User
    {
        private User() { }

        //异步上下文中保存用户属性
        private static AsyncLocal<User> _asyncUser = new AsyncLocal<User>();
        private static User _user
        {
            get
            {
                _asyncUser.Value = _asyncUser.Value ?? new User();
                return _asyncUser.Value;
            }
        }

        /// <summary>
        /// 当前登录用户信息
        /// </summary>
        public static User Current => _user;

        /// <summary>
        /// 用户IdString，使用string兼容多种数据类型: int、long、string
        /// </summary>
        public string IdString => _getIdString?.Invoke();

        /// <summary>
        /// 是否已认证身份
        /// </summary>
        public bool IsAuthenticated => _isAuthenticated == null ? IdString.IsNotNullOrEmptyOrWhiteSpace() : _isAuthenticated.Invoke();

        private static Func<string> _getIdString;
        private static Func<bool> _isAuthenticated;

        /// <summary>
        /// 注入获取用户Id的逻辑
        /// </summary>
        /// <param name="getIdString">获取用户Id逻辑</param>
        public static void RegisterIdString(Func<string> getIdString)
        {
            _getIdString = getIdString;
        }

        /// <summary>
        /// 注入判断用户是否登录的逻辑
        /// </summary>
        /// <param name="isAuthenticated">判断用户是否登录逻辑</param>
        public static void RegisterIsAuthenticated(Func<bool> isAuthenticated)
        {
            _isAuthenticated = isAuthenticated;
        }

        //异步上下文中保存用户属性
        private ConcurrentDictionary<string, object> _props = new ConcurrentDictionary<string, object>();

        /// <summary>
        /// 所有属性
        /// </summary>
        /// <returns></returns>
        public IReadOnlyDictionary<string, object> Properties => _props;

        /// <summary>
        /// 获取当前登录人属性
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="defaultValue">默认值</param>
        /// <returns></returns>
        public object GetProperty(string key, object defaultValue = null)
        {
            var flag = _props.TryGetValue(key, out object value);
            if (flag) return value;
            else return defaultValue;
        }

        /// <summary>
        /// 获取当前登录人属性
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="defaultValue">默认值</param>
        /// <returns></returns>
        public T GetProperty<T>(string key, T defaultValue = default(T))
        {
            var flag = _props.TryGetValue(key, out object value);
            if (flag) return value.ToWithDefault(defaultValue);
            else return defaultValue;
        }

        /// <summary>
        /// 是否存在指定的属性
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool ContainsProperty(string key)
        {
            return _props.ContainsKey(key);
        }

        /// <summary>
        /// 设置用户属性(添加或更新)
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public void SetProperty(string key, object value)
        {
            _props.AddOrUpdate(key, value, (key, old) => value);
        }

        /// <summary>
        /// 删除用户属性
        /// </summary>
        /// <param name="key"></param>
        public void DeleteProperty(string key)
        {
            _props.TryRemove(key, out _);
        }
    }
}
